/*
 * ==================================
 * auther: Mingo
 * date: 2025-1-25
 * ==================================
 */
#include "../include/keyboard.h"
#include "../include/dev.h"
#include "../include/arch.h"
#include "../include/error.h"
#include "../include/ipc.h"
#include "../include/string.h"
#include "../include/irq.h"
#include "../include/log.h"
#include "../include/keymap.h"

extern void exception_handler_kb();
skb_t kb_in;

static int code_with_E0 = 0;
static int column = 0;
static int shift_l = 0;
static int shift_r = 0;
static int alt_l = 0;
static int alt_r = 0;
static int ctrl_l = 0;
static int ctrl_r = 0;
static int caps_lock;
static int num_lock;
static int scroll_lock;

/* 键盘初始化 */
void keyboard_init()
{
    /* 设置键盘中断 */
    irq_install(0x21, exception_handler_kb);
    enable_8259(0x21);

    kb_in.count = 0;
    kb_in.phead = kb_in.ptail = kb_in.buf;
}

void do_handler_kb(exception_frame_t *frame)
{
    uchar scan_code = inb(0x60);    
    if (kb_in.count < KB_IN_BYTES) {
        *(kb_in.phead) = scan_code;
        kb_in.phead++;

        if (kb_in.phead == kb_in.buf + KB_IN_BYTES) {
            kb_in.phead = kb_in.buf;
        }
        kb_in.count++;
    }

    pic_done(0x21);
}

static uchar get_keyboard_buffer()
{
    uchar scan_code;

    while(kb_in.count <= 0) {}

    uint eflags = enter_critical();
    scan_code = *(kb_in.ptail);
    kb_in.ptail++;
    if (kb_in.ptail == kb_in.buf + KB_IN_BYTES) {
        kb_in.ptail = kb_in.buf;
    }
    kb_in.count--;
    leave_critical(eflags);

    return scan_code;
}

void kb_process(uint key)
{
    char result[2] = {'\0', '\0'};

    /* FLAG_EXT 标示可打印字符 */
    if (!(key & FLAG_EXT)) {
        result[0] = key & 0xFF;
        kprintf("%s\n", result);
    }
}

void keyboard_read()
{
    uchar scan_code;
    uint *keyrow;   /* 指向keymap的某一样，keymap每3个字符为一行 */
    int make;       /* 1 表示是按下，0 表示释放 */
    uint key = 0;   /* keyboard.h中定义的按键整型值 */

    if (kb_in.count > 0) {
        code_with_E0 = 0;
        scan_code = get_keyboard_buffer();

        /* 开始解析扫描码 */
        if (scan_code == 0xE1) {
            int i = 0;
            uchar pause_code[] = {0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5};

            int is_pause = 1;
            for (i=1; i<6; i++) {
                if (get_keyboard_buffer() != pause_code[i]) {
                    is_pause = 0;
                    break;
                }
            }

            if (is_pause) {
                key = PAUSEBREAK;
            }
        } else if (scan_code == 0xE0) {
            scan_code = get_keyboard_buffer();

            /* PrintScreen 被按下 */
            if (scan_code == 0x2A) {
                if (get_keyboard_buffer() == 0xE0) {
                    if (get_keyboard_buffer() == 0x37) {
                        key = PRINTSCREEN;
                        make = 1;
                    }
                }
            }
            /* PrintScreen 被释放 */
            if (scan_code == 0xB7) {
                if (get_keyboard_buffer() == 0xE0) {
                    if (get_keyboard_buffer() == 0xAA) {
                        key = PRINTSCREEN;
                        make = 0;
                    }
                }
            }

            /* 其他 */
            if (key == 0) {
                code_with_E0 = 1;
            }
        } 
        if ((key != PAUSEBREAK) && (key != PRINTSCREEN)) {
            /* 判断是按下还是释放 */
            make = (scan_code & FLAG_BREAK ? 0 : 1);

            keyrow = &keymap[(scan_code & 0x7F) * MAP_COLS];
            column = 0;

            if (shift_l || shift_r) {
                column = 1;
            }

            if (code_with_E0) {
                column = 2;
                code_with_E0 = 0;
            }

            key = keyrow[column];

            switch(key) {
                case SHIFT_L:
                    shift_l = make;
                    key = 0;
                    break;
                case SHIFT_R:
                    shift_r = make;
                    key = 0;
                    break;
                case CTRL_L:
                    ctrl_l = make;
                    key = 0;
                    break;
                case CTRL_R:
                    ctrl_r = make;
                    key = 0;
                    break;
                case ALT_L:
                    alt_l = make;
                    key = 0;
                    break;
                case ALT_R:
                    alt_r = make;
                    key = 0;
                    break;
                default:
                    /* 释放按键，不处理 */
                    if (make) {
                        key |= shift_l ? FLAG_SHIFT_L : 0;
                        key |= shift_r ? FLAG_SHIFT_R : 0;
                        key |= ctrl_l ? FLAG_CTRL_L : 0;
                        key |= ctrl_r ? FLAG_CTRL_R : 0;
                        key |= alt_l ? FLAG_ALT_L : 0;
                        key |= alt_r ? FLAG_ALT_R : 0;

                        kb_process(key);
                    }
            }
        }
    }
}
